#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <regex>
#include <sys/stat.h>


#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/base/Logging.h>
#include <functional>
#include <memory>
#include "protocol_tool.h"


namespace server_muduo{
extern std::unordered_map<int, std::string> _statu_msg;
extern std::unordered_map<std::string, std::string> _mime_msg;

#define DEFALT_TIMEOUT 10

    class HttpRequest {
        public:
            std::string _method;      
            std::string _path;        
            std::string _version;     
            std::string _body;        
            std::smatch _matches;     
            std::unordered_map<std::string, std::string> _headers;  //头部字段
            std::unordered_map<std::string, std::string> _params;   //查询字符串
        public:
            HttpRequest():_version("HTTP/1.1") {}
            void ReSet() {
                _method.clear();
                _path.clear();
                _version = "HTTP/1.1";
                _body.clear();
                std::smatch match;
                _matches.swap(match);
                _headers.clear();
                _params.clear();
            }
      
            void SetHeader(const std::string &key, const std::string &val) {
                _headers.insert(std::make_pair(key, val));
            }
    
            bool HasHeader(const std::string &key) const {
                auto it = _headers.find(key);
                if (it == _headers.end()) {
                    return false;
                }
                return true;
            }

            std::string GetHeader(const std::string &key) const {
                auto it = _headers.find(key);
                if (it == _headers.end()) {
                    return "";
                }
                return it->second;
            }
     
            void SetParam(const std::string &key, const std::string &val) {
                _params.insert(std::make_pair(key, val));
            }
     
            bool HasParam(const std::string &key) const {
                auto it = _params.find(key);
                if (it == _params.end()) {
                    return false;
                }
                return true;
            }
  
            std::string GetParam(const std::string &key) const {
                auto it = _params.find(key);
                if (it == _params.end()) {
                    return "";
                }
                return it->second;
            }

            size_t ContentLength() const {

                bool ret = HasHeader("Content-Length");
                if (ret == false) {
                    return 0;
                }
                std::string clen = GetHeader("Content-Length");
                return std::stol(clen);
            }
 
            bool Close() const {

                if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive") {
                    return false;
                }
                return true;
            }
    };

    class HttpResponse {
        public:
            int _statu;
            bool _redirect_flag;
            std::string _body;
            std::string _redirect_url;
            std::unordered_map<std::string, std::string> _headers;
        public:
            HttpResponse():_redirect_flag(false), _statu(200) {}
            HttpResponse(int statu):_redirect_flag(false), _statu(statu) {} 
            void SetStatu(int statu)
            {
                _statu = statu;
            }

            void ReSet() {
                _statu = 200;
                _redirect_flag = false;
                _body.clear();
                _redirect_url.clear();
                _headers.clear();
            }

            void SetHeader(const std::string &key, const std::string &val) {
                _headers.insert(std::make_pair(key, val));
            }

            bool HasHeader(const std::string &key) {
                auto it = _headers.find(key);
                if (it == _headers.end()) {
                    return false;
                }
                return true;
            }

            std::string GetHeader(const std::string &key) {
                auto it = _headers.find(key);
                if (it == _headers.end()) {
                    return "";
                }
                return it->second;
            }
            void SetContent(const std::string &body,  const std::string &type = "text/html") {
                _body = body;
                SetHeader("Content-Type", type);
            }
            void SetRedirect(const std::string &url, int statu = 302) {
                _statu = statu;
                _redirect_flag = true;
                _redirect_url = url;
            }

            bool Close() {

                if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive") {
                    return false;
                }
                return true;
            }
    };



    #define MAX_LINE 8192
    class HttpContext {
        private:
            int _resp_statu; 
            HttpRecvStatu _recv_statu; 
            HttpRequest _request;  
        private:
            bool ParseHttpLine(const std::string &line) {
                std::smatch matches;
                std::cout << line <<std::endl;
                std::regex e(R"(^\s*(GET|HEAD|POST|PUT|DELETE)\s+(?:https?://[^/]+)?(/[^\s?]*)(?:\?([^\s]*))?\s+(HTTP\/1\.[01])\s*$)", std::regex::icase);
                bool ret = std::regex_match(line, matches, e);
                if (ret == false) {

                    _recv_statu = RECV_HTTP_ERROR;
                    _resp_statu = 400;
                    return false;
                }
            
                _request._method = matches[1];
                std::cout << " _request._method" << _request._method<< std::endl;
                std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);
                
                _request._path = Util::UrlDecode(matches[2], false);
                std::cout << " _request._path" << _request._path<< std::endl;
                
                _request._version = matches[4];
                std::cout << " _request._version" << _request._version<< std::endl;
                
                std::vector<std::string> query_string_arry;
                std::string query_string = matches[3];

                
                
                Util::Split(query_string, "&", &query_string_arry);

                for (auto &str : query_string_arry) {
                    size_t pos = str.find("=");
                    if (pos == std::string::npos) {
                        _recv_statu = RECV_HTTP_ERROR;
                        _resp_statu = 400;
                        return false;
                    }
                    std::string key = Util::UrlDecode(str.substr(0, pos), true);  
                    std::string val = Util::UrlDecode(str.substr(pos + 1), true);
                    _request.SetParam(key, val);
                }
                return true;
            }
            bool RecvHttpLine(muduo::net::Buffer*buf) {
                if (_recv_statu != RECV_HTTP_LINE) return false;

            
                std::string line = Util::getLineAndPop(buf);
                

                if (line.size() == 0) {
                    
                    if (buf->readableBytes()> MAX_LINE) {
                        _recv_statu = RECV_HTTP_ERROR;
                        _resp_statu = 414;
                        return false;
                    }
                    
                    return true;
                }

                
                if (line.size() > MAX_LINE) {
                    _recv_statu = RECV_HTTP_ERROR;
                    _resp_statu = 414;
                    return false;
                }
     
                bool ret = ParseHttpLine(line);
                if (ret == false) {
                    return false;
                }

                _recv_statu = RECV_HTTP_HEAD;
                return true;
            }

            bool RecvHttpHead(muduo::net::Buffer *buf) {
                if (_recv_statu != RECV_HTTP_HEAD) return false;
                

                while(1){
                    std::string line = Util::getLineAndPop(buf);
                   
                    if (line.size() == 0) {
                        
                        if (buf->readableBytes() > MAX_LINE) {
                            _recv_statu = RECV_HTTP_ERROR;
                            _resp_statu = 414;
                            return false;
                        }
                        
                        return true;
                    }
                    
         
                    if (line.size() > MAX_LINE) {
                        _recv_statu = RECV_HTTP_ERROR;
                        _resp_statu = 414;//URI TOO LONG
                        return false;
                    }

                    if (line == "\n" || line == "\r\n") {
                        std::cout << "RecvHttpHead break"<< std::endl;
                        break;
                    }
                    bool ret = ParseHttpHead(line);
                    if (ret == false) {
                        return false;
                    }

                }

                _recv_statu = RECV_HTTP_BODY;
                return true;
            }
            bool ParseHttpHead(std::string &line) {

                if (line.back() == '\n') line.pop_back();
                if (line.back() == '\r') line.pop_back();
                size_t pos = line.find(": ");
                if (pos == std::string::npos) {
                    std::cout << "---ParseHttpHead---npos---error"<<std::endl;
                    _recv_statu = RECV_HTTP_ERROR;
                    _resp_statu = 400;//
                    return false;
                }
                std::string key = line.substr(0, pos);  
                std::string val = line.substr(pos + 2);
                _request.SetHeader(key, val);
                return true;
            }
            bool RecvHttpBody(muduo::net::Buffer *buf) {
                if (_recv_statu != RECV_HTTP_BODY) return false;


                size_t content_length = _request.ContentLength();
                if (content_length == 0) {
            
                    _recv_statu = RECV_HTTP_OVER;
                    return true;
                }
            
                size_t real_len = content_length - _request._body.size();
            
                if (buf->readableBytes() >= real_len) {
                    _request._body.append(buf->peek(), real_len);
                    buf->retrieve(real_len);
                    _recv_statu = RECV_HTTP_OVER;
                    return true;
                }
                
                _request._body.append(buf->peek(), buf->readableBytes());
                buf->retrieve(buf->readableBytes());
                return true;
            }
        public:
            HttpContext():_resp_statu(200), _recv_statu(RECV_HTTP_LINE) {}
            void ReSet() {
                _resp_statu = 200;
                _recv_statu = RECV_HTTP_LINE;
                _request.ReSet();
            }
            int RespStatu() { return _resp_statu; }
            HttpRecvStatu RecvStatu() { return _recv_statu; }
            HttpRequest &Request() { return _request; }

            void RecvHttpRequest(muduo::net::Buffer *buf) {
                
                switch(_recv_statu) {
                    case RECV_HTTP_LINE: RecvHttpLine(buf);
                    case RECV_HTTP_HEAD: RecvHttpHead(buf);
                    case RECV_HTTP_BODY: RecvHttpBody(buf);
                }
                return;
            }
    };
    
    
    class HttpTrieRouter {
    private:
        using Handler = std::function<void(const HttpRequest&, HttpResponse*)>;
        using Handlers = std::vector<std::pair<std::regex, Handler>>;
        struct TrieNode {
            std::unordered_map<std::string, std::unique_ptr<TrieNode>> children;
            Handler handler = nullptr;
            std::string param_key; 
            bool is_wildcard = false;
        };

        std::unique_ptr<TrieNode> root_;
        std::vector<std::pair<std::regex, Handler>> regex_routes_; 

        void InsertPath(const std::vector<std::string>& segments, Handler handler) {
            TrieNode* current = root_.get();
            
            for (const auto& segment : segments) {
                if (segment.size() > 1 && segment[0] == ':') {
                    current->param_key = segment.substr(1);
                    current->is_wildcard = true;
                    
                    if (!current->children[""]) {
                        current->children[""] = std::make_unique<TrieNode>();
                    }
                    current = current->children[""].get();
                } 
                else {
                    if (!current->children[segment]) {
                        current->children[segment] = std::make_unique<TrieNode>();
                    }
                    current = current->children[segment].get();
                }
            }
            
            current->handler = handler;
        }

    public:
        HttpTrieRouter() : root_(std::make_unique<TrieNode>()) {}
        void AddRoute(const std::string& method, const std::string& path, Handler handler) {
            if (path.find('*') != std::string::npos || 
                path.find('(') != std::string::npos) {
                regex_routes_.emplace_back(std::regex(path), handler);
                return;
            }

            std::vector<std::string> segments;
            size_t start = 0;
            while (start < path.size()) {
                size_t end = path.find('/', start);
                if (end == std::string::npos) end = path.size();
                if (end != start) { // 忽略空段
                    segments.push_back(path.substr(start, end - start));
                }
                start = end + 1;
            }

            InsertPath(segments, handler);
        }

        bool FindRoute(const std::string& path, HttpRequest& req, Handler& handler) {
            std::vector<std::string> segments;
            size_t start = 0;
            while (start < path.size()) {
                size_t end = path.find('/', start);
                if (end == std::string::npos) end = path.size();
                if (end != start) {
                    segments.push_back(path.substr(start, end - start));
                }
                start = end + 1;
            }

            TrieNode* current = root_.get();
            req._params.clear();

            for (const auto& segment : segments) {
        
                if (current->children.count(segment)) {
                    current = current->children[segment].get();
                } 
                else if (current->is_wildcard) {
                    req._params[current->param_key] = segment;
                    current = current->children[""].get();
                } 
                else {
                    return false;
                }
            }

            if (current && current->handler && 
                current->children.empty()) { 
                handler = current->handler;
                return true;
            }

            for (const auto& [regex, h] : regex_routes_) {
                if (std::regex_match(path, req._matches, regex)) {
                    handler = h;
                    return true;
                }
            }

            return false;
        }
    };


    class HttpServer {
    private:
        using Handler = std::function<void(const HttpRequest&, HttpResponse*)>;
        using Handlers = std::vector<std::pair<std::regex, Handler>>;
        
        struct MethodRouter {
            HttpTrieRouter trie;
            Handlers regex_handlers; 
        };
        std::unordered_map<std::string, MethodRouter> routers_;
        std::string basedir_;
        

        muduo::net::TcpServer server_;

    

    public:
        HttpServer(int port, const std::string& name,muduo::net::EventLoop* loop, int timeout = 30)
            : server_(loop, muduo::net::InetAddress(port), name) {
            
            server_.setConnectionCallback(
                std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
            server_.setMessageCallback(
                std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, 
                        std::placeholders::_2, std::placeholders::_3));
        }

    private:
        void ErrorHandler(const HttpRequest& req, HttpResponse* rsp) {
            std::string body;
            body += "<html>";
            body += "<head>";
            body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
            body += "</head>";
            body += "<body>";
            body += "<h1>";
            body += std::to_string(rsp->_statu);
            body += " ";
            body += Util::StatuDesc(rsp->_statu);
            body += "</h1>";
            body += "</body>";
            body += "</html>";
        
            rsp->SetContent(body, "text/html");
        }

        void WriteResponse(const muduo::net::TcpConnectionPtr& conn, 
                        const HttpRequest& req, HttpResponse& rsp) {

            std::cout << "WriteResponse" <<std::endl;
            if (req.Close()) {
                rsp.SetHeader("Connection", "close");
            } else {
                rsp.SetHeader("Connection", "keep-alive");
            }
            
            if (!rsp._body.empty() && !rsp.HasHeader("Content-Length")) {
                rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));
            }
            
            if (!rsp._body.empty() && !rsp.HasHeader("Content-Type")) {
                rsp.SetHeader("Content-Type", "application/octet-stream");
            }
            
            if (rsp._redirect_flag) {
                rsp.SetHeader("Location", rsp._redirect_url);
            }

            std::stringstream rsp_str;
            rsp_str << req._version << " " << std::to_string(rsp._statu) << " " 
                    << Util::StatuDesc(rsp._statu) << "\r\n";
            
            for (auto& head : rsp._headers) {
                rsp_str << head.first << ": " << head.second << "\r\n";
            }
            
            rsp_str << "\r\n";
            rsp_str << rsp._body;

            std::cout << "conn->send" <<std::endl;
            std::cout << rsp_str.str() <<std::endl;
            conn->send(rsp_str.str());
        }

        bool IsFileHandler(const HttpRequest& req) {
            if (basedir_.empty()) return false;
            if (req._method != "GET" && req._method != "HEAD") return false;
            if (!Util::ValidPath(req._path)) return false;
            
            std::string req_path = basedir_ + req._path;
            if (req._path.back() == '/') {
                req_path += "index.html";
            }
            
            return Util::IsRegular(req_path);
        }

        void FileHandler(const HttpRequest& req, HttpResponse* rsp) {
            std::string req_path = basedir_ + req._path;
            if (req._path.back() == '/') {
                req_path += "index.html";
            }
            
            if (Util::ReadFile(req_path, &rsp->_body)) {
                rsp->SetHeader("Content-Type", Util::ExtMime(req_path));
            }
        }

        void Dispatcher(HttpRequest& req, HttpResponse* rsp,const std::string& method ) {
            //使用前缀树进行优化
            std::cout << "===Dispatcher===" <<std::endl;
            Handler handler;
            if (routers_[method].trie.FindRoute(req._path, req, handler)) {
                return handler(req, rsp);
            }
            
       
            for (auto& route : routers_[method].regex_handlers) {
                if (std::regex_match(req._path, req._matches, route.first)) {
                    return route.second(req, rsp);
                }
            }
            
        
            rsp->_statu = 404;
            LOG_WARN << "No route for: " << req._path;
            std::cout <<"===========================" <<std::endl;
            std::cout <<"req._method: " <<req._method <<std::endl;
            std::cout<<"req_path: "<<req._path<<std::endl;
            std::cout<<"req._body: "<<req._body <<std::endl;
            for(auto e : req._headers)
            {
                std::cout<< e.first <<" "<<e.second <<std::endl;
            }
            for(auto e : req._params)
            {
                std::cout<< e.first <<" "<<e.second <<std::endl;
            }
            std::cout <<"===========================" <<std::endl;

           
        }

        void Route(HttpRequest& req, HttpResponse* rsp) {
            std::cout << "=====Route====="<<std::endl;
            if (IsFileHandler(req)) {
                std::cout << "=====FileHandler====="<<std::endl;
                return FileHandler(req, rsp);
            }
            Dispatcher(req, rsp,req._method);
        }
       

        void OnConnected(const muduo::net::TcpConnectionPtr& conn) {
            if (conn->connected()) {
                // 初始化连接上下文
                conn->setContext(HttpContext());
                LOG_INFO << "New connection: " << conn->name();
            } else {
                LOG_INFO << "Connection closed: " << conn->name();
            }
        }

        void OnMessage(const muduo::net::TcpConnectionPtr& conn, 
                    muduo::net::Buffer* buffer, 
                    muduo::Timestamp) {


            std::cout << "=== Raw Request ===" << std::endl;
            std::cout << std::string(buffer->peek(), buffer->readableBytes()) << std::endl;
            std::cout << "===================" << std::endl;
            
        
            HttpContext* context = boost::any_cast<HttpContext>(conn->getMutableContext());
            
            std::cout << "context->RecvHttpRequest" << std::endl;
            context->RecvHttpRequest(buffer);
            HttpRequest& req = context->Request();
            HttpResponse rsp(context->RespStatu());
            
            std::cout << "context->RespStatu()" << context->RespStatu() <<std::endl;

            if (context->RespStatu() >= 400) {
                ErrorHandler(req, &rsp);
                WriteResponse(conn, req, rsp);
                context->ReSet();
                buffer->retrieveAll(); 
                conn->shutdown();
                return;
            }
            
            if (context->RecvStatu() != RECV_HTTP_OVER) {
                return;
            }
            
            Route(req, &rsp);
            WriteResponse(conn, req, rsp);
            
            
            if (rsp.Close()) {
                conn->shutdown();
            }
            context->ReSet();
            
        }

        bool IsRegexPattern(const std::string& pattern) {
           
            return pattern.find('*') != std::string::npos || 
                pattern.find('(') != std::string::npos ||
                pattern.find('[') != std::string::npos;
        }
    public:
        void SetBaseDir(const std::string& path) {
            assert(Util::IsDirectory(path));
            basedir_ = path;
        }
        void Get(const std::string& pattern, const Handler& handler) {
            if (IsRegexPattern(pattern)) {
                routers_["GET"].regex_handlers.emplace_back(std::regex(pattern), handler);
            } else {
                routers_["GET"].trie.AddRoute("GET", pattern, handler);
            }
        }
        
        void Post(const std::string& pattern, const Handler& handler) {
            if (IsRegexPattern(pattern)) {
                routers_["POST"].regex_handlers.emplace_back(std::regex(pattern), handler);
            } else {
                routers_["POST"].trie.AddRoute("POST", pattern, handler);
            }
        }
        
        void Put(const std::string& pattern, const Handler& handler) {
            if (IsRegexPattern(pattern)) {
                routers_["PUT"].regex_handlers.emplace_back(std::regex(pattern), handler);
            } else {
                routers_["PUT"].trie.AddRoute("PUT", pattern, handler);
            }
        }
        
        void Delete(const std::string& pattern, const Handler& handler) {
            if (IsRegexPattern(pattern)) {
                routers_["DELETE"].regex_handlers.emplace_back(std::regex(pattern), handler);
            } else {
                routers_["DELETE"].trie.AddRoute("DELETE", pattern, handler);
            }
        }
        
        void SetThreadCount(int count) {
            server_.setThreadNum(count);
        }
        
        void Listen() {
            server_.start();
        }
    };
}